package concurrent;

import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;

/**
 * TODO 简单说明
 *
 * @author yangyang
 * @date 2017/7/14 10:51
 * @email kekecany@163.com
 */
class Toast {
    enum Status {DRY, BUTTERED, JAMMED}

    private int id;
    private Status status = Status.DRY;

    public Toast(int id) {
        this.id = id;
    }

    public void buttered() {
        status = Status.BUTTERED;
    }

    public void jamed() {
        status = Status.JAMMED;
    }

    public int getId() {
        return id;
    }

    public Status status() {
        return status;
    }

    @Override
    public String toString() {
        return "Toast{" +
                "id=" + id +
                ", status=" + status +
                '}';
    }
}

class ToastQueue extends LinkedBlockingQueue<Toast> {
}

class Toaster implements Runnable {
    private ToastQueue dryQueue;
    private Random random = new Random(47);
    private int count = 0;
    public Toaster(ToastQueue dryQueue) {
        this.dryQueue = dryQueue;
    }
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                TimeUnit.MILLISECONDS.sleep(100 + random.nextInt(500));
                Toast toast = new Toast(count++);
                System.out.println("toaster: " + toast);
                dryQueue.put(toast);
            }
        } catch (InterruptedException e) {
            System.out.println("toast interruption");
        }
        System.out.println("exit toaster");
    }
}

class Butter implements Runnable {
    private ToastQueue dryQueue, butteredQueue;
    public Butter(ToastQueue dryQueue, ToastQueue butteredQueue) {
        this.dryQueue = dryQueue;
        this.butteredQueue = butteredQueue;
    }
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Toast toast = dryQueue.take();
                toast.buttered();
                System.out.println("butter: " + toast);
                butteredQueue.put(toast);
            }
        }catch (InterruptedException e) {
            System.out.println("butter interruption");
        }
        System.out.println("exit Butter");
    }
}

class Jamer implements Runnable {
    private ToastQueue butteredQueue, finishedQueue;
    public Jamer(ToastQueue butteredQueue, ToastQueue finishedQueue) {
        this.butteredQueue = butteredQueue;
        this.finishedQueue = finishedQueue;
    }
    @Override
    public void run() {
        try {
            while (!Thread.interrupted()) {
                Toast toast = butteredQueue.take();
                toast.jamed();
                System.out.println("jammer: " + toast);
                finishedQueue.put(toast);
            }
        }catch (InterruptedException e) {
            System.out.println("jammed interruption");
        }
        System.out.println("exit jamer");
    }
}

class Eater implements Runnable {

    private ToastQueue finishedQueue;

    private int counter = 0;

    public Eater(ToastQueue finishedQueue) {
        this.finishedQueue = finishedQueue;
    }

    @Override
    public void run() {
        try{
            while (!Thread.interrupted()) {
                Toast toast = finishedQueue.take();
                if (toast.getId() != counter++ || toast.status() != Toast.Status.JAMMED) {
                    System.err.println(">>>>>Error");
                    System.exit(1);
                }else {
                    System.out.println("Chomp: " + toast);
                }
            }
        } catch (InterruptedException e) {
            System.out.println("eat interruption");
        }
        System.out.println("exit eater");
    }
}

public class ToastOMatic {

    public static void main(String[] args) throws InterruptedException {
        ToastQueue dryQueue = new ToastQueue(),
                butterQueue = new ToastQueue(),
                finishQueue = new ToastQueue();
        ExecutorService executorService = Executors.newCachedThreadPool();
        executorService.execute(new Toaster(dryQueue));
        executorService.execute(new Butter(dryQueue, butterQueue));
        executorService.execute(new Jamer(butterQueue, finishQueue));
        executorService.execute(new Eater(finishQueue));
        TimeUnit.SECONDS.sleep(5);
        executorService.shutdownNow();
    }
}
